﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Vbe.Interop;
using System.Windows;
using System.Windows.Controls;

namespace An_Im.Model
{
    /// <summary>
    /// VBA 函数块类型
    /// </summary>
    enum VBACodeLineType
    {
        PrivateSub, PublicSub, PublicSubWithZeroPara, PublicSubWithOnePara, Other, Comment
    }

    /// <summary>
    /// 用于控制 VBA 对象的静态类。
    /// </summary>
    static class VBAManipulator
    {
        /// <summary>
        /// 获得 VB 对象。
        /// </summary>
        public static VBProject VBProject
        {
            get
            {
                try
                {
                    return CoreManipulator.App.ActivePresentation.VBProject;
                }
                catch
                {
                    System.Windows.Forms.MessageBox.Show(
                        "您需要在选项中打开对 VBA 模型的访问许可。\n请注意！行业内普遍不支持用户开放 VBA 模型访问许可，特别是对于 Excel 插件来说尤为重要，" +
                        "但是 PowerPoint 而言，PPT 无自启动执行宏的基础，PPT 宏病毒的实现可能性较小，请谨慎思考是否照做。\n" +
                        "最合理的实现方案是使用 VBIDE 插件开发，实现 VSTO 和 VBIDE 两插件的进程通信实现数据的传输。",
                        "警告", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning);
                    throw new Exception("您需要在选项中打开对 VBA 模型的访问许可");
                }
            }
        }

        /// <summary>
        /// 过得主进程的 VB 项目。
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetMainProcsInCurrentVBProject()
        {
            if (VBProject == null) return null;
            var list = new List<string>();
            for (int i = 1; i <= VBProject.VBComponents.Count; i++)
            {
                var procrange = GetMainProcsInComponent(VBProject.VBComponents.Item(i));
                if (procrange != null)
                    list.AddRange(procrange);
            }
            return list;
        }

        /// <summary>
        /// 获得指定模块的宏代码。
        /// </summary>
        public static IEnumerable<string> GetMainProcsInComponent(VBComponent component)
        {
            if (component == null) return null;
            if (component.Type == vbext_ComponentType.vbext_ct_StdModule)
            {
                return GetMainProcsInCodeModule(component.CodeModule);
            }
            else
                return null;

        }

        private static IEnumerable<string> GetMainProcsInCodeModule(CodeModule codeModele)
        {
            var list = new List<string>();
            int i = codeModele.CountOfDeclarationLines + 1;

            int count = codeModele.CountOfLines;
            while (i < count)
            {
                var name = codeModele.ProcOfLine[i, out vbext_ProcKind kind];

                var line = codeModele.ProcCountLines[name, kind];

                if (kind == vbext_ProcKind.vbext_pk_Proc)
                {
                    var procname = GetMainProc(codeModele, i, line, name);// 获取动态步长
                    if (procname != null)
                        list.Add(procname);
                }

                i += line;
            }

            return list;
        }

        /// <summary>
        /// 在区域内获得显式的 Sub 宏。 
        /// </summary>
        /// <param name="codeModule"></param>
        /// <param name="start"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        private static string GetMainProc(CodeModule codeModule, int start, int count, string defaultName)
        {
            for (int i = start; i < start + count; i++)
            {
                var linestr = codeModule.Lines[i, 1];
                if (AnalyzeLine(linestr) == VBACodeLineType.PublicSub)
                {
                    if (GetParamterCount(linestr) == 0)
                        return defaultName;
                    else
                        return null;
                }

            }
            return null;
        }

        /// <summary>
        /// 分析行信息。
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static VBACodeLineType AnalyzeLine(string str)
        {
            for (int i = 0; i < str.Length && i < 20; i++)
            {
                if (str[i] == ' ') continue;
                else if (str[i] == '\'') return VBACodeLineType.Comment;
                else if (str[i] == 'P' || str[i] == 'p')
                {
                    if (IsNextString(str, i, "ublic Sub"))
                    {
                        return VBACodeLineType.PublicSub;
                    }
                    else if (IsNextString(str, i, "rivate Sub"))
                    {
                        return VBACodeLineType.PrivateSub;
                    }
                }
                else if ((str[i] == 'S' || str[i] == 's') && IsNextString(str, i, "ub")) return VBACodeLineType.PublicSub;
                else break;
            }


            return VBACodeLineType.Other;
        }

        // 获得 Sub 的参数数
        private static int GetParamterCount(string str)
        {
            int i = 0;
            int start = 0;
            for (i = start; i < str.Length; i++)
            {
                if (str[i] == ' ') continue;
                else if (str[i] == '(')
                    break;
            }
            start = i;
            int comma = 0;
            bool hasPara = false;
            for (i = start + 1; i < str.Length; i++)
            {
                if (str[i] == ' ') continue;
                else if (str[i] == ')')
                    break;
                else if (str[i] == ',')
                    comma++;
                else
                    hasPara = true;

            }
            if (hasPara == true && comma == 0) return 1;
            else if (hasPara == false && comma == 0) return 0;
            else
                return comma + 1;
        }

        // 指定位置后是否跟有该内容
        private static bool IsNextString(string str, int start, string pattern)
        {
            for (int i = start + 1; i <= (start + pattern.Length) && i <= str.Length; i++)
            {
                if (char.ToLower(str[i]) != char.ToLower(pattern[i - start - 1])) return false;
            }
            return true;
        }

        public static void Write(string vbacode)
        {
            try
            {
                var vbproject = VBProject;
                VBComponent component = null;
                if (vbproject.VBComponents.Count == 0)
                {
                    component = vbproject.VBComponents.Add(vbext_ComponentType.vbext_ct_StdModule);
                }
                else
                {
                    component = vbproject.VBComponents.Item(1);
                }
                component.CodeModule.AddFromString(vbacode);
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
                System.Windows.Forms.MessageBox.Show("您需要在设置里解禁 VBA 模型的访问权限。");
            }
        }

        public static void Write(string module, string vbacode)
        {
            try
            {
                var vbproject = VBProject;
                var component = FindComponentByName(module);
                if (component == null) component = AddComponent(module, string.Empty);
                component.CodeModule.AddFromString(vbacode);
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
                System.Windows.Forms.MessageBox.Show("您需要在设置里解禁 VBA 模型的访问权限。");
            }
        }

        /// <summary>
        /// 添加模块。
        /// </summary>
        public static VBComponent AddComponent(string name, string code, bool replace = true)
        {
            if (replace)
            {
                var com = FindComponentByName(name);
                if(com == null)
                {
                    com = VBProject.VBComponents.Add(vbext_ComponentType.vbext_ct_StdModule);
                    com.Name = name;

                }
                var linecount = com.CodeModule.CountOfLines;
                com.CodeModule.DeleteLines(1, linecount);
                com.CodeModule.AddFromString(code);

                return com;
            }

            else
            {
                var newname = name;
                var random = new Random();
                while (true)
                {
                    var com = FindComponentByName(newname);
                    if (com == null)
                        break;
                    else
                        newname = name + random.Next(10000, 99999);
                }
                var newcom = VBProject.VBComponents.Add(vbext_ComponentType.vbext_ct_StdModule);
                newcom.Name = newname;
                var linecount = newcom.CodeModule.CountOfLines;
                newcom.CodeModule.DeleteLines(1, linecount);
                newcom.CodeModule.AddFromString(code);
                return newcom;

            }

        }

        /// <summary>
        /// 通过名称查找组件。
        /// </summary>
        public static VBComponent FindComponentByName(string name)
        {
            var count = VBProject.VBComponents.Count; 
            for (int i = 1; i <= count; i++)
            {
                if (VBProject.VBComponents.Item(i).Name == name)
                    return VBProject.VBComponents.Item(i);
            }
            return null;
        }



        public static string GetAllCode(this CodeModule codeModule)
        {
            return codeModule.Lines[1, codeModule.CountOfLines];
        }

    }
}
